use std::time::Duration;

use objc2_ui_kit::{UIEvent, UIKey, UIKeyModifierFlags, UIPress, UIPressPhase, UITouch, UITouchPhase};

use crate::win::Factor;

use super::{WinRef, Window};

pub struct WinEvent<'a>(pub &'a UIEvent);
impl<'a> types::IntoEvt<Window, types::Mouse> for WinEvent<'a> {
	fn to_evt(&self, _: WinRef<'_>) -> Option<types::Mouse> {
		// NOT SURPORT
		None
	}
}
impl<'a> types::IntoEvt<Window, types::Wheel> for WinEvent<'a> {
	fn to_evt(&self, _win: WinRef<'_>) -> Option<types::Wheel> {
		// NOT SURPORT
		None
	}
}
pub struct WinKeyboard<'a>(pub &'a UIKey, pub &'a UIPress);
impl<'a> types::IntoEvt<Window, types::Keyboard> for WinKeyboard<'a> {
	fn to_evt(&self, _: WinRef<'_>) -> Option<types::Keyboard> {
		let press = self.1;
		let evt = self.0;
		let state = match unsafe { press.phase() } {
			UIPressPhase::Began => types::State::Down,
			UIPressPhase::Ended => types::State::Up,
			_ => return None,
		};
		let code = conv_code(evt);
		let key = conv_key(evt, code);
		let location = code.into();
		let metas = conv_metas(evt);
		let repeat = false;
		let composing = false;

		Some(types::Keyboard::new(key, code, state, location, metas, repeat, composing))
	}
}
pub struct WinTouch<'a>(pub u8, pub &'a UITouch);
impl<'a> types::IntoEvt<Window, types::Touch> for WinTouch<'a> {
	fn to_evt(&self, win: WinRef<'_>) -> Option<types::Touch> {
		let point = {
			let point = self.1.locationInView(None);
			let factor = Factor::from(win.1);
			(factor.to_f32(point.x), factor.to_f32(point.y))
		};
		let phase = match self.1.phase() {
			UITouchPhase::Began => types::Phase::Start,
			UITouchPhase::Moved => types::Phase::Update,
			UITouchPhase::Ended => types::Phase::End,
			UITouchPhase::Cancelled => types::Phase::Cancel,
			_ => return None,
		};
		let nanos = self.1.timestamp() * 1_000_000_000.;
		let time = Duration::from_nanos(nanos as _);

		let force = 0;
		Some(types::Touch::new(types::Button::Touch(self.0), phase, point, force, time))
	}
}
fn conv_metas(evt: &UIKey) -> types::Metas {
	let mut metas = types::Metas::EMPTY;
	let flags = unsafe { evt.modifierFlags() };
	use UIKeyModifierFlags as F;
	if flags.contains(F::UIKeyModifierShift) {
		metas |= types::Metas::SHIFT;
	}
	if flags.contains(F::UIKeyModifierControl) {
		metas |= types::Metas::CONTROL;
	}
	if flags.contains(F::UIKeyModifierAlternate) {
		metas |= types::Metas::ALT;
	}
	if flags.contains(F::UIKeyModifierCommand) {
		metas |= types::Metas::META;
	}
	metas
}
fn conv_key(evt: &UIKey, code: types::Code) -> types::Key {
	let chars = unsafe { evt.characters() };
	if chars.length() > 0 {
		return types::Key::Character(chars.to_string().into());
	}
	let chars = unsafe { evt.charactersIgnoringModifiers() };
	if chars.length() > 0 {
		return types::Key::Character(chars.to_string().into());
	}
	code.into()
}
fn conv_code(evt: &UIKey) -> types::Code {
	let scancode = unsafe { evt.keyCode() };
	use types::Code as KeyCode;
	// TODO
	KeyCode::Unidentified(scancode.0 as _)
}
